﻿using DataConvertLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace YWM.Communication
{
    /// <summary>
    /// 模型工具
    /// </summary>
    public static class DataConvertorUtil
    {
        #region 布尔属性
        /// <summary>
        /// 根据字节数组获取布尔模型对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">数据</param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public static T GetBoolModelFromBytes<T>(byte[] data, int count) where T : class, new()
        {
            if (data == null)
            {
                return default(T);
            }

            bool[] bools = BitLib.GetBitArrayFromByteArray(data, 0, count);
            T t = Activator.CreateInstance<T>();
            PropertyInfo[] propertyInfos = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.PropertyType.Name != "Boolean")
                {
                    continue;
                }

                ModelPropertyAttribute modelPropertyAttribute = propertyInfo.GetCustomAttribute<ModelPropertyAttribute>();
                if (modelPropertyAttribute == null)
                {
                    continue;
                }

                int startIndex = modelPropertyAttribute.StartIndex;
                if (startIndex >= count)
                {
                    break;
                }
                propertyInfo.SetValue(t, bools[startIndex]);
            }

            return t;
        }

        /// <summary>
        /// 根据布尔模型对象获取布尔数组
        /// </summary>
        /// <param name="model">布尔模型对象</param>
        /// <returns></returns>
        public static bool[] GetBoolsFromBoolModel<T>(T model) where T : class, new()
        {
            PropertyInfo[] propertyInfos = model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            Dictionary<int, bool> dics = new Dictionary<int, bool>(propertyInfos.Length);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (propertyInfo.PropertyType.Name != "Boolean")
                {
                    continue;
                }

                ModelPropertyAttribute attribute = propertyInfo.GetCustomAttribute<ModelPropertyAttribute>();
                if (attribute == null)
                {
                    continue;
                }
                bool value = (bool)propertyInfo.GetValue(model, null);
                dics.Add(attribute.StartIndex, value);
            }

            bool[] bools = dics.OrderBy(dic => dic.Key).Select(dic => dic.Value).ToArray();
            return bools;
        }
        #endregion

        #region 多属性
        /// <summary>
        /// 根据数据获取模型对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">字节数组</param>
        /// <param name="separator">分隔符</param>
        /// <param name="dataFormat">数据格式</param>
        /// <returns></returns>
        public static T GetModelFromBytes<T>(byte[] data, DataFormat dataFormat = DataFormat.ABCD) where T : class, new()
        {
            if (data == null)
            {
                return default(T);
            }

            T model = Activator.CreateInstance<T>();
            PropertyInfo[] propertyInfos = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                ModelMultiPropertyAttribute attribute = propertyInfo.GetCustomAttribute<ModelMultiPropertyAttribute>();
                if (attribute == null)
                {
                    continue;
                }

                object value = GetOneValueFromBytes(data, attribute, dataFormat);
                if (value == null)
                {
                    continue;
                }

                propertyInfo.SetValue(model, value);
            }

            return model;
        }

        /// <summary>
        /// 获取单个值
        /// </summary>
        /// <param name="data">字节数组</param>
        /// <param name="attribute">模型多属性特性</param>
        /// <param name="dataFormat">数据格式</param>
        /// <returns></returns>
        public static object GetOneValueFromBytes(byte[] data, ModelMultiPropertyAttribute attribute, DataFormat dataFormat = DataFormat.ABCD)
        {
            if (attribute == null)
            {
                return null;
            }
            return GetOneValueFromBytes(data, attribute.DataType, attribute.StartIndex, attribute.OffsetOrLength, dataFormat);
        }

        /// <summary>
        /// 获取单个值
        /// </summary>
        /// <param name="data">字节数组</param>
        /// <param name="dataType">数据类型</param>
        /// <param name="startIndex">开始索引</param>
        /// <param name="offsetOrLength">偏移或长度</param>
        /// <param name="dataFormat">数据格式</param>
        /// <returns></returns>
        public static object GetOneValueFromBytes(byte[] data, DataType dataType, int startIndex = 0, int offsetOrLength = 0, DataFormat dataFormat = DataFormat.ABCD)
        {
            object value = null;
            switch (dataType)
            {
                case DataType.Bool:
                    value = BitLib.GetBitFrom2BytesArray(data, startIndex, offsetOrLength, dataFormat == DataFormat.BADC || dataFormat == DataFormat.DCBA);
                    break;
                case DataType.Byte:
                    value = ByteLib.GetByteFromByteArray(data, dataFormat == DataFormat.BADC || dataFormat == DataFormat.DCBA ? startIndex : startIndex + 1);
                    break;
                case DataType.Short:
                    value = ShortLib.GetShortFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.Int:
                    value = IntLib.GetIntFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.Long:
                    value = LongLib.GetLongFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.UShort:
                    value = UShortLib.GetUShortFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.UInt:
                    value = UIntLib.GetUIntFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.ULong:
                    value = ULongLib.GetULongFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.Float:
                    value = FloatLib.GetFloatFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.Double:
                    value = DoubleLib.GetDoubleFromByteArray(data, startIndex, dataFormat);
                    break;
                case DataType.String:
                    value = StringLib.GetStringFromByteArrayByEncoding(data, startIndex, offsetOrLength, Encoding.UTF8);
                    break;
                case DataType.HexString:
                    value = StringLib.GetHexStringFromByteArray(data, startIndex, offsetOrLength);
                    break;
                case DataType.ByteArray:
                    value = ByteArrayLib.GetByteArrayFromByteArray(data, startIndex, offsetOrLength);
                    break;
            }
            return value;
        }

        /// <summary>
        /// 根据模型对象获取字节数组
        /// </summary>
        /// <param name="model">模型对象</param>
        /// <param name="dataFormat">数据格式</param>
        /// <returns></returns>
        public static byte[] GetBytesFromModel<T>(T model, DataFormat dataFormat = DataFormat.ABCD) where T : class, new()
        {
            PropertyInfo[] propertyInfos = model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            Dictionary<int, byte[]> dics = new Dictionary<int, byte[]>(propertyInfos.Length);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                ModelMultiPropertyAttribute attribute = propertyInfo.GetCustomAttribute<ModelMultiPropertyAttribute>();
                if (attribute == null)
                {
                    continue;
                }

                object value = propertyInfo.GetValue(model, null);
                byte[] propertyBytes = GetBytesFromOneValue(attribute.DataType, value, attribute.StartIndex, attribute.OffsetOrLength, dataFormat);
                if (propertyBytes != null)
                {
                    dics.Add(attribute.StartIndex, propertyBytes);
                }
            }

            IEnumerable<byte[]> bytess = dics.OrderBy(dic => dic.Key).Select(dic => dic.Value);
            List<byte> bytes = new List<byte>();
            foreach (byte[] item in bytess)
            {
                bytes.AddRange(item);
            }
            return bytes.ToArray();
        }

        /// <summary>
        /// 根据单个值获取字节数组(仅支持数据类型和字符串类型)
        /// </summary>
        /// <param name="dataType">数据类型</param>
        /// <param name="value">值</param>
        /// <param name="startIndex">开始索引</param>
        /// <param name="length">长度</param>
        /// <param name="dataFormat">数据格式</param>
        /// <returns></returns>
        public static byte[] GetBytesFromOneValue(DataType dataType, object value, int startIndex = 0, int length = 0, DataFormat dataFormat = DataFormat.ABCD)
        {
            byte[] data = null;
            switch (dataType)
            {
                case DataType.Byte:
                    data = ByteArrayLib.GetByteArrayFromByte(Convert.ToByte(value));
                    break;
                case DataType.Short:
                    data = ByteArrayLib.GetByteArrayFromShort(Convert.ToInt16(value), dataFormat);
                    break;
                case DataType.Int:
                    data = ByteArrayLib.GetByteArrayFromInt(Convert.ToInt32(value), dataFormat);
                    break;
                case DataType.Long:
                    data = ByteArrayLib.GetByteArrayFromLong(Convert.ToInt64(value), dataFormat);
                    break;
                case DataType.UShort:
                    data = ByteArrayLib.GetByteArrayFromUShort(Convert.ToUInt16(value), dataFormat);
                    break;
                case DataType.UInt:
                    data = ByteArrayLib.GetByteArrayFromUInt(Convert.ToUInt32(value), dataFormat);
                    break;
                case DataType.ULong:
                    data = ByteArrayLib.GetByteArrayFromULong(Convert.ToUInt64(value), dataFormat);
                    break;
                case DataType.Float:
                    data = ByteArrayLib.GetByteArrayFromFloat(Convert.ToSingle(value), dataFormat);
                    break;
                case DataType.Double:
                    data = ByteArrayLib.GetByteArrayFromDouble(Convert.ToDouble(value), dataFormat);
                    break;
                case DataType.String:
                    data = ByteArrayLib.GetByteArrayFromString(value.ToString(), Encoding.UTF8);
                    break;
                case DataType.HexString:
                    data = ByteArrayLib.GetByteArrayFromHexString(value.ToString());
                    break;
                case DataType.ByteArray:
                    data = ByteArrayLib.GetByteArrayFromByteArray((byte[])value, startIndex, length);
                    break;
            }
            return data;
        }
        #endregion
    }
}
